#pragma once
#include "Image.h"
#include <iostream>

typedef signed char int8;

namespace DL {

	template<class T>
	struct Tensor1D
	{
	private:
		T* datas;
	public:
		int width;
	public:
		Tensor1D() {}
		Tensor1D(const Tensor1D& tensor)
		{
			datas = new T[tensor.width];
			memcpy(datas, tensor.datas, sizeof(T) * tensor.width);
		}
		Tensor1D(int width)
		{
			this->width = width;
			this->datas = new T[width];
		}
		~Tensor1D()
		{
			delete[] datas;
		}
		T& At(int x)
		{
			return datas[x];
		}
		T Get(int x) const
		{
			return datas[x];
		}
		const T* GetDataPtr()const {
			return datas;
		}
		void Set(int x, T value)
		{
			datas[x] = value;
		}
		void PrintOut() const
		{
			std::cout << "tensor1D:\n";
			for (int x = 0; x < width; x++)
			{
				std::cout.width(4);
				std::cout << (float)Get(x) << ',';
			}
			std::cout << std::endl;
		}
	};

	template<class T>
	struct Tensor2D
	{
	private:
		T* datas;
	public:
		int width;
		int height;
	public:
		Tensor2D() {}
		Tensor2D(const Tensor2D& tensor)
		{
			int count = tensor.width * tensor.height;
			datas = new T[count];
			memcpy(datas, tensor.datas, sizeof(T) * count);
		}
		Tensor2D(int width, int height)
		{
			this->width = width;
			this->height = height;
			this->datas = new T[width * height];
		}
		~Tensor2D()
		{
			delete[] datas;
		}
		T& At(int x, int y)
		{
			return datas[width * y + x];
		}
		T Get(int x, int y) const
		{
			return datas[width * y + x];
		}
		const T* GetDataPtr()const {
			return datas;
		}

		void Set(int x, int y, T value)
		{
			datas[width * y + x] = value;
		}
	};

	template<class T>
	struct Tensor3D
	{
	private:
		T* datas;
	public:
		int width;
		int height;
		int channel;
		int framePiexlCount;
	public:
		Tensor3D() {}
		Tensor3D(const Tensor3D& tensor)
		{
			int count = tensor.width * tensor.height * tensor.channel;
			datas = new T[count];
			memcpy(datas, tensor.datas, sizeof(T) * count);
		}
		Tensor3D(int width, int height, int channel)
		{
			this->width = width;
			this->height = height;
			this->channel = channel;
			this->framePiexlCount = width * height;
			this->datas = new T[channel * width * height];
		}
		Tensor3D(Image& img)
		{
			this->width = img.width;
			this->height = img.height;
			this->channel = 3;
			this->framePiexlCount = width * height;
			this->datas = new T[channel * width * height];
			for (int y = 0; y < height; y++)
			{
				for (int x = 0; x < width; x++)
				{
					Image::Piexl piexl = img.GetPiexl(x, y);
					Set(x, y, 0, piexl.red / 255.0f);
					Set(x, y, 1, piexl.green / 255.0f);
					Set(x, y, 2, piexl.blue / 255.0f);
				}
			}
		}
		~Tensor3D()
		{
			delete[] datas;
		}
		T& At(int x, int y, int c)
		{
			return datas[c * framePiexlCount + width * y + x];
		}
		T Get(int x, int y, int c) const
		{
			return datas[c * framePiexlCount + width * y + x];
		}
		const T* GetDataPtr()const {
			return datas;
		}

		void Set(int x, int y, int c, T value)
		{
			datas[c * framePiexlCount + width * y + x] = value;
		}

		void PrintOut() const
		{
			for (int c = 0; c < channel; c++)
			{
				std::cout << "channel:" << c << std::endl;
				for (int y = 0; y < height; y++)
				{
					for (int x = 0; x < width; x++)
					{
						std::cout.width(4);
						std::cout << (float)Get(x, y, c) << ',';
					}
					std::cout << std::endl;
				}
			}
		}
	};

	template<class T>
	struct Tensor4D
	{
	private:
		T* datas;
	public:
		int width;
		int height;
		int channel;
		int deepth;
		int framePiexlCount;
		int chanelPiexlCount;
	public:
		Tensor4D() {}
		Tensor4D(const Tensor4D& tensor)
		{
			int count = tensor.width * tensor.height * tensor.channel * tensor.deepth;
			datas = new T[count];
			memcpy(datas, tensor.datas, sizeof(T) * count);
		}
		Tensor4D(int width, int height, int channel, int deepth)
		{
			this->width = width;
			this->height = height;
			this->channel = channel;
			this->deepth = deepth;
			this->framePiexlCount = width * height;
			this->chanelPiexlCount = framePiexlCount * channel;
			this->datas = new T[channel * width * height * deepth];
		}
		~Tensor4D()
		{
			delete[] datas;
		}
		T& At(int x, int y, int c, int d)
		{
			return datas[d * chanelPiexlCount + c * framePiexlCount + width * y + x];
		}
		T Get(int x, int y, int c, int d) const
		{
			return datas[d * chanelPiexlCount + c * framePiexlCount + width * y + x];
		}
		const T* GetDataPtr()const {
			return datas;
		}

		void Set(int x, int y, int c, int d, T value)
		{
			datas[d * chanelPiexlCount + c * framePiexlCount + width * y + x] = value;
		}
	};

	struct QATParams {
	public:
		float scale;
		int8 zero;
	};
}
